package com.exit66.jukebox.servlet;

import java.io.File;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FilenameFilter;
import java.io.IOException;
import java.io.InputStream;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.ServletOutputStream;

import com.exit66.jukebox.Options;
import com.exit66.jukebox.data.Album;
import com.exit66.jukebox.data.Track;
import com.exit66.jukebox.tag.TagReader;
import com.exit66.jukebox.tag.TagReaderFactory;
import com.exit66.jukebox.util.MD5;

public class AlbumCoverImageServlet extends HttpServlet {
		
	/**
	 * 
	 */
	private static final long serialVersionUID = 9000823110084145958L;

	public void doGet(HttpServletRequest req, HttpServletResponse res)
	throws ServletException, IOException 
	{
		ServletOutputStream out;
		
		out = res.getOutputStream();
		
		if (req.getParameter("albumid") == null) {
			sendBlankImage(res, out);
			return;
		}
			
		int albumId = Integer.parseInt(req.getParameter("albumid"));
		Album album = Album.getAlbumFromId(albumId);
		if (album == null) {
			sendBlankImage(res, out);
			return;
		}
		
		if (sendCachedImage(album, res, out)) 
			return;
		
		res.setHeader("Last-Modified", Options.getCurrentUTCTime());
		
		if (sendTagImage(album, res, out)) {
			return;
		}
		
		if (sendFolderImage(album, res, out)) {
			return;
		}
		
		sendBlankImage(album, res, out);
				
		out.close();
	}
	
	protected boolean sendCachedImage(Album album, HttpServletResponse res, ServletOutputStream out) {
		String cachedName;
		try {
			cachedName = getCachedFileName(album);
			if (cachedName == null)
				return false;
			
			File cachedFile = new File(cachedName);
			File cachedFileType = new File(cachedName + "-t");
			if (cachedFile.exists()) {
				if (cachedFileType.exists()) {
					DataInputStream ds = new DataInputStream(new FileInputStream(cachedFileType));
					res.setContentType(ds.readUTF());
				}
				else {
					res.setContentType("image/jpeg");
				}
				res.setHeader("Last-Modified", Options.getFileTime(cachedFile.lastModified()));
				writeFile(cachedFile, out);
				return true;
			}
			
		}
		catch (Exception e) {
			System.err.println(e);
			return false;
		}
		
		return false;
	}
	
	protected String getCachedFileName(Album album) {
		try {
			MD5 md5 = new MD5(album.getAlbumName());
			return Options.getCacheDirectory() + album.getAlbumID() + "-" + md5.compute() + "-cache";
		}
		catch (Exception e) {
			return null;
		}
	}
	
	protected boolean sendTagImage(Album album, HttpServletResponse res, ServletOutputStream out) {
		try {
			Track track = album.getFirstTrackForAlbum();
			if (track == null) 
				return false;
			TagReader tag = TagReaderFactory.getTagReader(track.getFullFileName());
			if ((tag.loadCoverImage()) && (tag.getCoverMimeType().length() != 0)) {
				res.setContentType(tag.getCoverMimeType());
				byte[] data = tag.getCoverImage();
				out.write(data);
				writeCachedImage(album, data, tag.getCoverMimeType());
				return true;
			}
		}
		catch (Exception e) {
			System.err.println(e);
		}
		return false;
	}
	
	protected boolean sendFolderImage(Album album, HttpServletResponse res, ServletOutputStream out) {
        try {
        	
			Track track = album.getFirstTrackForAlbum();
			if (track == null) 
				return false;
			File file = new File(track.getFullFileName());
		    ImageFilter filter = new ImageFilter();
            
            File directory = file.getParentFile();
            String[] images = directory.list(filter);
            
            if ((images != null) && (images.length >= 1)) {
                String upperCaseName = images[0].toUpperCase();
                String contentType = "";
                if (upperCaseName.endsWith(".JPG") ||
                        upperCaseName.endsWith(".JPEG")) {
                    contentType = "image/jpeg";
                } else if (upperCaseName.endsWith(".PNG")) {
                    contentType = "image/png";
                } else if (upperCaseName.endsWith(".GIF")) {
                    contentType = "image/gif";
                }
                res.setContentType(contentType);
                writeFile(new File(directory.getPath(), images[0]), out, album, contentType);
                return true;
            }
            
        } catch (Exception e) {
        	System.err.println(e);
        }
        return false;

	}
	
	protected void sendBlankImage(HttpServletResponse res, ServletOutputStream out) {
		sendBlankImage(null, res, out);
	}
	
	protected void sendBlankImage(Album album, HttpServletResponse res, ServletOutputStream out) {
		File blankImage = new File(Options.getDefaultImageFileName());
		if (blankImage.exists()) {
			res.setContentType(Options.getDefaultImageMimeType());
			if (album != null) {
				writeFile(blankImage, out, album, Options.getDefaultImageMimeType());
			}
			else {
				writeFile(blankImage, out);
			}
		}
	}
	
	protected void writeCachedImage(Album album, byte[] data, String mimeType) {
		String cacheFileName = getCachedFileName(album);
		if (cacheFileName != null) {
			writeOutputToFile(cacheFileName, data);
			writeCachedImageFileType(cacheFileName, mimeType);
		}
	}
	
	protected void writeCachedImageFileType(String cacheFileName, String mimeType) {
		File file = new File(cacheFileName + "-t");
		try {
			DataOutputStream ds = new DataOutputStream(new FileOutputStream(file));
			ds.writeUTF(mimeType);
			ds.close();
		}
		catch (Exception e) {
			System.err.println(e);
		}
	}
	
	protected void writeFile(File file, ServletOutputStream out) {
		writeFile(file, out, null, null);
	}
	
    protected void writeFile(File file, ServletOutputStream out, Album cacheForAlbum, String mimeType) {
        
        try {
        	File cacheFile = null;
        	FileOutputStream cacheFs = null;
        	String cacheFileName;
        	if (cacheForAlbum != null) {
        		cacheFileName = getCachedFileName(cacheForAlbum);
        		cacheFile = new File(cacheFileName);
        		cacheFs = new FileOutputStream(cacheFile);
        		writeCachedImageFileType(cacheFileName, mimeType);
        	}
            InputStream in = new FileInputStream(file);
            byte[] tmp = new byte[1024];
            int bytes;
            while ((bytes = in.read(tmp)) != -1) {
                out.write(tmp, 0, bytes);
                if (cacheFile != null)
                	cacheFs.write(tmp);
            }
            if (cacheFile != null)
            	cacheFs.close();
        } catch (FileNotFoundException e) {
            System.err.println(e);
        } catch (IOException e) {
            System.err.println(e);
        }
        
    }
    
	protected void writeOutputToFile(String fileName, byte[] out) {
		try {
			File file = new File(fileName);
			FileOutputStream fs = new FileOutputStream(file);
			fs.write(out);
			fs.close();
		}
		catch (IOException e) {
			System.err.println(e);
		}
	}
}

class ImageFilter implements FilenameFilter {
    public boolean accept(File dir, String name) {
        String upperCaseName = name.toUpperCase();
        return (upperCaseName.endsWith(".JPG") ||
                upperCaseName.endsWith(".JPEG") ||
                upperCaseName.endsWith(".PNG") ||
                upperCaseName.endsWith(".GIF"));
    }
}
